﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Compilex.CodeGeneration
{
    internal class CommonOperations
    {
        private CodeGenerator Generator { get; set; }
        private StringBuilder customBuilder = null;
        private StringBuilder CodeBuilder
        {
            get
            {
                return customBuilder == null ? Generator.CodeBuilder : customBuilder;
            }
        }
        public CommonOperations(CodeGenerator generator)
        {
            this.Generator = generator;
        }

        public CommonOperations(CodeGenerator generator, StringBuilder codeBuilder)
        {
            this.Generator = generator;
            this.customBuilder = codeBuilder;
        }

        internal void Move(string labelTo, string labelFrom)
        {
            Comment(String.Format("mov {0}, {1}\r\n", labelTo, labelFrom));
            Load(labelFrom);
            StoreTo(labelTo);
        }

        internal void Comment(string comment)
        {
            CodeBuilder.AppendLine(" ; " + comment);
        }

        internal void StoreTo(int constant, string labelTo)
        {
            Load(constant);
            CodeBuilder.AppendLine("MM " + labelTo);
        }

        internal string StoreAccToTemp()
        {
            Generator.Labels.IncrementResultsLabel();
            string tempLabel = Generator.Labels.GetResultLabel();
            StoreTo(tempLabel);
            return tempLabel;
        }

        internal string StoreAccToResult()
        {
            Generator.Labels.IncrementResultsLabel();
            string tempLabel = Generator.Labels.GetResultLabel();
            StoreTo(tempLabel);
            return tempLabel;
        }

        internal void StoreTo(string labelTo)
        {
            CodeBuilder.AppendLine("MM " + labelTo);
        }

        internal void Load(string operandLabel)
        {
            CodeBuilder.AppendLine("LD " + operandLabel);
        }

        internal void MoveToGlobalVariable(string varName)
        {
            StoreTo(Generator.Labels.GetGlobalVariableLabel(varName));
        }

        internal void LoadGlobalVariable(string varName)
        {
            Load(Generator.Labels.GetGlobalVariableLabel(varName));
        }
        internal void LoadAddress(string operandLabel)
        {
            CodeBuilder.AppendLine("LV " + operandLabel);
        }

        internal void Load(string operandLabel, string instructionLabel )
        {
            CodeBuilder.AppendLine(instructionLabel + " LD " + operandLabel);
        }
        internal void Load(int constant)
        {
            CodeBuilder.AppendLine("LD " + Generator.Labels.GetConstantLabel(constant));
        }
        internal void Load(int constant, string instructionLabel)
        {
            CodeBuilder.AppendLine(instructionLabel+ " LD " + Generator.Labels.GetConstantLabel(constant));
        }


        /// <summary>
        /// Põe resultado no acumulador
        /// </summary>
        /// <param name="label"></param>
        /// <param name="constant"></param>
        internal void Sub(string label, int constant)
        {
            //Comment("AC = " + label + " - " + constant);
            Load(label);
            CodeBuilder.AppendLine("- " + Generator.Labels.GetConstantLabel(constant));
        }

        internal void Add(string label1, string label2)
        {
            //Comment("AC = " + label1 + " + " + label2);
            Load(label1);
            CodeBuilder.AppendLine("+ " + label2);
        }

        internal void Add(string label)
        {
            CodeBuilder.AppendLine("+ " + label);
        }

        internal void Add(int constant)
        {
            Add(Generator.Labels.GetConstantLabel(constant));
        }

        internal void Add(string label, int constant)
        {
            Comment("AC = " + label + " + " + constant);
            Load(label);
            CodeBuilder.AppendLine("+ " + Generator.Labels.GetConstantLabel(constant));
        }

        internal void MoveToPointer(string labelToPointer, string labelContent)
        {
            Comment("mov *" + labelToPointer + ", " + labelContent);

            string executionLabel = Generator.Labels.GetAssignLabel();

            Add(labelToPointer, Labels.IndirectMoveStub);
            StoreTo(executionLabel);
            Load(labelContent);
            CodeBuilder.AppendLine(executionLabel + " K /0 ; executa mov");
        }


        internal void MoveAddressToPointer(string labelToPointer, string labelFrom)
        {
            Comment("mov *" + labelToPointer + ", " + labelFrom);

            string executionLabel = Generator.Labels.GetAssignLabel();

            Add(labelToPointer, Labels.IndirectMoveStub);
            StoreTo(executionLabel);
            LoadAddress(labelFrom);
            CodeBuilder.AppendLine(executionLabel + " K /0 ; executa mov");
        }


        internal void MoveToPointer(string labelToPointer, int offset, string labelContent)
        {
            Comment("mov *(" + labelToPointer + " + " + offset + "), " + labelContent);

            string executionLabel = Generator.Labels.GetAssignLabel();
            if (offset > 0)
                Add(labelToPointer, offset);
            else
                Sub(labelToPointer, -offset);

            Add(Labels.IndirectMoveStub);
            StoreTo(executionLabel);
            Load(labelContent);
            CodeBuilder.AppendLine(executionLabel + " K /0 ; executa mov");
        }

        internal void LoadFromPointer(string labelPointer, int offset)
        {
            Comment("load *(" + labelPointer + " + " + offset +")");

            string executionLabel = Generator.Labels.GetAssignLabel();
            if (offset > 0)
                Add(labelPointer, offset);
            else
                Sub(labelPointer, -offset);

            Add(Labels.IndirectLoadStub);
            StoreTo(executionLabel);
            CodeBuilder.AppendLine(executionLabel + " K /0 ; executa load");
        }


        internal void Nop()
        {
            Load(0);
        }

        internal void Nop(string label)
        {
            Load(0, label);
        }

        internal void JumpTo(string label)
        {
            CodeBuilder.AppendLine("JP " + label);
        }

        internal void JumpToAddressInAcc()
        {
            Comment("jmp *acc");
            string executionLabel = Generator.Labels.GetAssignLabel();
            StoreTo(executionLabel);
            CodeBuilder.AppendLine(executionLabel + " K /0 ; executa jump");
        }

        internal string JumpZero()
        {
            string label = Generator.Labels.GetAssignLabel();
            JumpZero(label);
            return label;
        }

        internal void JumpZero(string whileExitLabel)
        {
            CodeBuilder.AppendLine("JZ " + whileExitLabel);
        }

        internal string JumpNegative()
        {
            string label = Generator.Labels.GetAssignLabel();
            CodeBuilder.AppendLine("JN " + label);
            return label;
        }


    }
}
